package com.hnit.FiltersFactory;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.google.gson.Gson;
import com.hnit.utils.JwtTokenUtil;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.Data;
import lombok.extern.java.Log;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.security.Key;
import java.util.*;

@Component
@Log
public class GlobalAuthFilter implements GlobalFilter, Ordered {
    @Value("${token.secret}")
    private String secret;

    private final AppProperties appProperties;
    private final JwtTokenUtil jwtTokenUtil;
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    public GlobalAuthFilter(AppProperties appProperties, JwtTokenUtil jwtTokenUtil) {
        this.appProperties = appProperties;
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        // 获取请求路径
        String path = request.getURI().getPath();
        log.info("请求路径: " + path);

        // 检查是否是白名单路径
        if (isExcludedPath(path)) {
            log.info("路径在白名单中，直接放行");
            return chain.filter(exchange);
        }

        // 检查token
        String token = request.getHeaders().getFirst("token");
        if (StringUtils.isEmpty(token)) {
            log.warning("请求头中没有找到token");
            return unauthorizedResponse(response, "请先登录系统");
        }

        try {
            // 验证token有效性
            if (!isTokenValidate(token)) {
                log.warning("token验证失败");
                return unauthorizedResponse(response, "登录已过期，请重新登录");
            }

            // 提取用户信息并添加到请求头
            String userId = String.valueOf(jwtTokenUtil.extractUserId(token));
            Set<String> roleset = jwtTokenUtil.extractRoles(token);
            String roles = String.join(",", roleset);

            log.info("用户ID: " + userId + ", 角色: " + roles);

            ServerHttpRequest modifiedRequest = request.mutate()
                    .header("X-User-ID", userId)
                    .header("X-User-Roles", roles)
                    .build();

            return chain.filter(exchange.mutate().request(modifiedRequest).build());

        } catch (ExpiredJwtException e) {
            log.severe("Token已过期: " + e.getMessage());
            return unauthorizedResponse(response, "登录已过期，请重新登录");
        } catch (JwtException e) {
            log.severe("Token无效: " + e.getMessage());
            return unauthorizedResponse(response, "无效的登录凭证");
        } catch (Exception e) {
            log.severe("系统错误: " + e.getMessage());
            return errorResponse(response, "系统错误，请稍后再试");
        }
    }

    private boolean isExcludedPath(String path) {
        for (String excludePath : appProperties.getExcludePaths()) {
            if (pathMatcher.match(excludePath, path)) {
                return false; // 返回false表示需要认证
            }
        }
        return true; // 返回true表示不需要认证
    }

    private boolean isTokenValidate(String token) {
        try {
            Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
            Jws<Claims> claimsJws = Jwts.parserBuilder()
                    .setSigningKey(key)
                    .build()
                    .parseClaimsJws(token);

            Claims claims = claimsJws.getBody();
            Date expiration = claims.getExpiration();
            Date now = new Date();

            if (expiration != null && expiration.before(now)) {
                return false;
            }
            return true;
        } catch (JwtException e) {
            throw e; // 抛出异常让上层处理
        }
    }

    private Mono<Void> unauthorizedResponse(ServerHttpResponse response, String message) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return errorResponse(response, message);
    }

    private Mono<Void> errorResponse(ServerHttpResponse response, String message) {
        response.getHeaders().add("Content-Type", "application/json;charset=utf-8");
        Map<String, Object> map = new HashMap<>();
        map.put("code", HttpStatus.UNAUTHORIZED.value());
        map.put("msg", message);
        map.put("success", false);

        Gson gson = new Gson();
        String json = gson.toJson(map);
        DataBuffer buffer = response.bufferFactory().wrap(json.getBytes());
        return response.writeWith(Flux.just(buffer));
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

@Component
@ConfigurationProperties(prefix = "rights")
@Data
class AppProperties {
    private List<String> excludePaths;
}